﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using TagLib;

namespace asoptions
{
    class Tagger
    {
        private MainWindow window;
        public string tmpFolderPath { get; set; }
        public string currentSongPath { get; set; }
        public LinkedList<Song> currentSong { get; set; }
        public TagOptions currentOps { get; set; }
        public int monoBasePts { get; set; }
        public int minMatchSize { get; set; }
        public int perWhiteBlock { get; set; }
        public int matchTicks { get; set; }
        public int rowCount { get; set; }
        public string monoChar { get; set; }
        public string monoGreys { get; set; }
        public string cameraMode { get; set; }

        public Tagger(MainWindow win)
        {
            window = win;

            tmpFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            tmpFolderPath = System.IO.Path.Combine(tmpFolderPath, ".asoptions\\music");

            if (!System.IO.Directory.Exists(tmpFolderPath))
            {
                System.IO.Directory.CreateDirectory(tmpFolderPath);
            }

            currentSongPath = monoChar = cameraMode = "";
            currentSong = new LinkedList<Song>();
        }

        public bool removeSong(Song s)
        {
            foreach (var node in currentSong)
            {
                if (s.Equals(node))
                {
                    currentSong.Remove(node);
                    return true;
                }
            }
            return false;
        }

        public bool editingSong(Song s)
        {
            return currentSong.Contains(s);
        }

        public bool editingSong(string fileName)
        {
            foreach (var node in currentSong)
            {
                string nodefn = node.fileName;
                int index = nodefn.LastIndexOf("-tmp");
                if (index >= 0)
                    nodefn = node.fileName.Remove(index, 8);
                if (fileName == node.fileName)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Checks for currently selected options and returns a string that can be placed in a song's Title tag.
        /// </summary>
        /// <returns>Correctly formatted string intended for a song's Title tag</returns>
        public string getOpsString()
        {
            string ops = "";
            if ((currentOps & TagOptions.FourLane) == TagOptions.FourLane)
                ops += " [as-4lane]";
            if ((currentOps & TagOptions.CharMonoRestr) == TagOptions.CharMonoRestr)
                ops += monoChar;
            if ((currentOps & TagOptions.MonoGray) == TagOptions.MonoGray)
                ops += monoGreys;
            if ((currentOps & TagOptions.MonoBasePts) == TagOptions.MonoBasePts)
                ops += (" [as-monopt" + monoBasePts + "]");
            if ((currentOps & TagOptions.NoStealthBonus) == TagOptions.NoStealthBonus)
                ops += " [as-nostlth]";
            if ((currentOps & TagOptions.Camera) == TagOptions.Camera)
                ops += cameraMode;
            if ((currentOps & TagOptions.LongChains) == TagOptions.LongChains)
                ops += " [as-caterp]";
            if ((currentOps & TagOptions.MinMatchSize) == TagOptions.MinMatchSize)
                ops += (" [as-msz" + minMatchSize + "]");
            if ((currentOps & TagOptions.PerWhiteBlock) == TagOptions.PerWhiteBlock)
                ops += (" [as-wb" + perWhiteBlock + "]");
            if ((currentOps & TagOptions.MatchTicks) == TagOptions.MatchTicks)
                ops += (" [as-mt" + matchTicks + "]");
            if ((currentOps & TagOptions.RowCount) == TagOptions.RowCount)
                ops += (" [as-prows" + rowCount + "]");
            if ((currentOps & TagOptions.InvGrid) == TagOptions.InvGrid)
                ops += " [as-hidepuz]";
            if ((currentOps & TagOptions.IncSpeed) == TagOptions.IncSpeed)
                ops += " [as-steep]";
            if ((currentOps & TagOptions.Portal) == TagOptions.Portal)
                ops += " [as-portal]";

            return ops;
        }

        /// <summary>
        /// Edit's the title tag of a file and returns the file (without saving).
        /// </summary>
        /// <param name="path">Full path to the file</param>
        /// <param name="ops">Options for the Title tag. Use getOpsString().</param>
        /// <returns>An unsaved TagLib.File with ops added to it's Title tag</returns>
        private TagLib.File getSongFile(string path, string ops)
        {
            TagLib.File file = TagLib.File.Create(path);
            int tmp = file.Tag.Title.IndexOf(" [as-");
            if (tmp > 0)
                file.Tag.Title = file.Tag.Title.Substring(0, tmp).Trim();
            file.Tag.Title = file.Tag.Title + ops;
            return file;
        }

        /// <summary>
        /// Updates all current songs with the current options.
        /// </summary>
        public void saveCurrent()
        {
            string ops = getOpsString();
            var node = currentSong.First;
            while (node != null)
            {
                TagLib.File file = getSongFile(node.Value.fullPath, ops);
                try
                {
                    window.addToLog("[Saving \"" + node.Value + "\"]", MessageType.Info);
                    file.Save();
                    node = node.Next;
                }
                catch
                {
                    FileStream fsIn = new FileStream(node.Value.fullPath, FileMode.Open, FileAccess.Read);
                    string p = node.Value.fullPath;
                    Random rand = new Random();
                    int r = rand.Next(1000, 2000);
                    p = p.Insert(p.LastIndexOf('.'), "-tmp" + r);
                    FileStream fsOut = new FileStream(p, FileMode.Create);
                    fsIn.CopyTo(fsOut);
                    fsIn.Close();
                    fsOut.Close();
                    TagLib.File f = getSongFile(p, ops);
                    window.addToLog("[Couldn't save \"" + node.Value + "\" (open somewhere else?); Saving to \"" + p + "\"]", MessageType.Error);
                    f.Save();
                    Song s = node.Value;
                    window.fileNameListBox_Remove(s);
                    s.fullPath = p;
                    s.fileName = s.fileName.Insert(s.fileName.LastIndexOf('.'), "-tmp" + r);
                    node.Value = s;
                    window.fileNameListBox_Add(s);
                    node = node.Next;
                }
            }
            currentSongPath = currentSong.First.Value.fullPath;
        }

        public string getCurrentTitle()
        {
            TagLib.File file = TagLib.File.Create(currentSongPath);
            return file.Tag.Title;
        }

        /// <summary>
        /// Returns titles of all current songs.
        /// </summary>
        /// <returns>List of all current song titles</returns>
        public List<string> getTitles()
        {
            List<string> titles = new List<string>();
            foreach (var path in currentSong)
            {
                TagLib.File file = TagLib.File.Create(path.fullPath);
                titles.Add(file.Tag.Title);
            }
            return titles;
        }

        public string getTitle(TagLib.File file)
        {
            string title = file.Tag.Title;
            int tmp = title.IndexOf(" [as-");
            if (tmp > 0)
                title = title.Substring(0, tmp).Trim();
            return title;
        }

        /// <summary>
        /// Extract special options from a file's title tag
        /// </summary>
        /// <param name="file">File from which to extract options</param>
        /// <returns>String array containing extracted options</returns>
        public static string[] getOptions(TagLib.File file)
        {
            string title = file.Tag.Title;
            int ind = title.IndexOf(" [as-");
            if (ind < 0 || title == String.Empty)
                return null;
            title = title.Substring(ind);
            char[] separators = { ' ', '[', ']' };
            string[] result = title.Split(separators, StringSplitOptions.RemoveEmptyEntries);
            return result;
        }
    }
}
